package dk.itu.samd.group2.tagapastryClient;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.Vector;

import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
import org.apache.commons.math.stat.descriptive.summary.Sum;

import rice.p2p.commonapi.Id;

import dk.itu.samd.group2.tagapastry.messages.*;

public class MessageCollector {
		
	// Let's make it basic
	private int counter;
	
	// Count number of messages in total
	private int sentMessages;

	// Bytes sent per message
	private final int NUM_OF_MSGS = 12;
	private Vector<Integer> bytesSent;
	private Vector<Integer> msgs;
	//private Hashtable<Id,Integer> msgsPerNode;
	private Hashtable<Id,Integer> bytesPerNode;
	//private Hashtable<Id,Vector<TagapastryMsg>> searchMsgs;
	
	//private int totalBytesSent;
	private int buffered;
	
	/*private String[] names = {"TagMsg","SearchMsg", //1-2
			"ResultsMsg","UntagMsg", // 3-4
			"IndexExpansionMsg","IndexRetractionRequestMsg", // 5-6
			"IndexRetractionResponseMsg","IndexExpansionConfirmationMsg", // 7-8
			"MaintenanceMsg","UpdateSizeMsg", // 9-10
			"ReplicationMsg","ReplicationMaintenanceMsg" // 11
	};*/
	
	public MessageCollector() {
		this.bytesSent = new Vector<Integer>(NUM_OF_MSGS);
		this.msgs = new Vector<Integer>(NUM_OF_MSGS);
		//this.msgsPerNode = new Hashtable<Id, Integer>();
		this.bytesPerNode = new Hashtable<Id, Integer>();
		//this.searchMsgs = new Hashtable<Id, Vector<TagapastryMsg>>();
	
		
		for(int i =0; i < NUM_OF_MSGS; i++) {
			this.bytesSent.add(new Integer(0));
			this.msgs.add(new Integer(0));
		}
	}
	
	public void resetTotalMessagesSent() {
		this.sentMessages = 0;
		//this.totalBytesSent = 0;
		this.counter = 0;
		
		for(int i = 0; i < NUM_OF_MSGS; i++) {
			bytesSent.set(i, 0);
			msgs.set(i, 0);
		}
		
		// Flush hash table as well, release memory before
		int keysize = this.bytesPerNode.keySet().size();
		int valsize = this.bytesPerNode.values().size();
		
		Id[] nodes = this.bytesPerNode.keySet().toArray(new Id[keysize]);
		Integer[] vecs = (Integer[])this.bytesPerNode.values().toArray(new Integer[valsize]);
		
		nodes = null;
		vecs = null;
		
		this.bytesPerNode.clear();
		/*
		keysize = this.searchMsgs.keySet().size();
		valsize = this.searchMsgs.values().size();
		
		nodes = this.searchMsgs.keySet().toArray(new Id[keysize]);
		Vector<TagapastryMsg>[] msgs = (Vector<TagapastryMsg>[])this.searchMsgs.values().toArray(new Vector[valsize]);
		
		nodes = null;
		msgs = null;
		this.searchMsgs.clear();
		*/
		/*keysize = msgsPerNode.keySet().size();
		valsize = msgsPerNode.values().size();
		
		Id[] node2 = this.msgsPerNode.keySet().toArray(new Id[keysize]);
		Integer[] i2 = (Integer[])this.msgsPerNode.values().toArray(new Integer[valsize]);
		
		node2 = null;
		i2 = null;
		
		this.msgsPerNode.clear();*/
	}
	
	public synchronized void incTotalSent() {
		++sentMessages;
	}
	
	public synchronized void decTotalSent() {
		--sentMessages;
	}
	
	public int getTotalSent() {
		return sentMessages;
	}
	
	public synchronized void decSent() {
		--counter;
	}
	
	public boolean dataReceived() {
		return counter == 0;
	}
	
	public synchronized int getCounter() {
		return counter;
	}
	
	public void status() {
		System.out.println("Messages stored: " + counter);
	}
	
	public void incMsg(short TYPE) {
		int size = msgs.get(getIndexBasedOnType(TYPE));
		size++;
		msgs.set(getIndexBasedOnType(TYPE), size);

		++counter;
	}
	
	int getIndexBasedOnType(int type) {
		return type-1;
	}
	
	public synchronized void incBytesSent(TagapastryMsg msg) {
		
		// Increment bytes sent per message
		int size = this.bytesSent.get(getIndexBasedOnType(msg.getType()));
		size += msg.getSize();
		this.bytesSent.set(getIndexBasedOnType(msg.getType()), size);
		
		Id id = msg.getSenderId();
		
		Integer in = this.bytesPerNode.get(id);
		if(in == null) {
			in = new Integer(0);
		}
		in += msg.getSize();
		
		// Store items in a table
		if(this.bytesPerNode.containsKey(id)) {
			this.bytesPerNode.remove(id);
		}
		
		this.bytesPerNode.put(id, in);
		
		//updateSearchMsgTime(msg);
		
		/*Integer i = this.msgsPerNode.get(id);
		if(i == null) {
			i = new Integer(0);
		}
		i++;

		if(this.msgsPerNode.containsKey(id))
			this.msgsPerNode.remove(id);
		
		this.msgsPerNode.put(id, i);*/
	}
	
	public void updateSearchMsgTime(TagapastryMsg msg){
		// Add search msgs
		// 2 - Search
		// 3 - Result
		/*if(msg.getType() == 2 || msg.getType() == 3) {
			Id id = msg.getSenderId();
			//msg.SEND_OR_DEL_TIME = System.currentTimeMillis();
			//msg.TIME = new Date();
			if(msg.getType() == 3)
				msg.TIME = new Date();
			
			//System.out.println("Adding time for message type: " + msg.getType());
			Vector<TagapastryMsg> smsgs = this.searchMsgs.get(id);
			if(smsgs == null) {
				smsgs = new Vector<TagapastryMsg>();
			}
			
			smsgs.add(msg);
			if(this.searchMsgs.containsKey(id)) {
				this.searchMsgs.remove(id);
			}
			
			this.searchMsgs.put(id, smsgs);
		}*/
	}
	
	public void printBytesSentAll(int ret, int exp) {
		printSummary("Number of bytes sent by all messages: ",ret, exp);
	}
	
	private void printSummary(String header, int ret, int exp) {
		try
		{			
			int sum=0;
			for(int msg=0; msg < NUM_OF_MSGS; msg++) {
				sum += bytesSent.get(msg);
			}
			
			// Print bytes per node
			Id[] nodes = this.bytesPerNode.keySet().toArray(new Id[this.bytesPerNode.keySet().size()]);

			long sumbytes=0;
			double[] data = new double[nodes.length];
			int minBytes = Integer.MAX_VALUE;
			int maxBytes = 0;
			for(int i=0; i < nodes.length; i++) {
				// Log output for each node for each message

				Integer bytes = this.bytesPerNode.get(nodes[i]);
				sumbytes += bytes;
				data[i] = bytes;
				if(bytes < minBytes)
					minBytes = bytes;
				
				if(bytes > maxBytes)
					maxBytes = bytes;
			}
			
			StandardDeviation sd = new StandardDeviation();
			double stdev = sd.evaluate(data);
			
			Sum sumator = new Sum();
			double ressum = sumator.evaluate(data);
			
			// Append
			FileWriter fw = new FileWriter("results.txt",true);
			BufferedWriter bw = new BufferedWriter(fw);
			
			bw.write(String.format("%d %d %d %d %f %f SENT\r\n",
					ret,exp,
					minBytes,maxBytes,
					stdev,ressum/data.length));
			
			
			// Print search / result msgs per node
			/*
			int keysize = this.searchMsgs.keySet().size();
			nodes = this.searchMsgs.keySet().toArray(new Id[keysize]);
			
			for(int i=0; i<nodes.length;i++) {
				Vector<TagapastryMsg> smsgs = this.searchMsgs.get(nodes[i]);

				long startTime = 0;
				long stopTime = 0;
				for(int j=0; j<smsgs.size(); j++) {
					TagapastryMsg msg = smsgs.get(j);
					if(msg.getType() == 2) {
						// Search msg
						startTime = msg.TIME.getTime();
					}
					else {
						if(msg.TIME.getTime() > stopTime)
							stopTime = msg.TIME.getTime();
					}
				}
				
				// Compute and display difference
				//if(stopTime >= startTime) {
					//System.out.println("Start: " + startTime + " End: " + stopTime);
					//System.out.println(String.format("%s %d",nodes[i],stopTime-startTime));
				//}
				//else
					//System.out.println("Negative: " + startTime);
			}*/
			
			bw.write(String.format("Delay is: %f Num of msgs %d \r\n",TestDataContainer.getListener().getAverageDelay(),TestDataContainer.getListener().getNumOfMsgsPassed()));
			
			bw.flush();
			bw.close();
		}
		catch(IOException ioex) {
			ioex.printStackTrace();
		}
	}
	
	public synchronized void incBuff() {
		this.buffered++;
	}
	
	public synchronized void decBuff() {
		this.buffered--;
	}
	
	public synchronized boolean isBufferEmpty() {
		return this.buffered == 0;
	}
}
